package terra.model.object;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import terra.Config;
import terra.database.DatabaseFactory;
import terra.definitions.NpcDef;
import terra.managers.DefManager;
import terra.managers.MapManager;
import terra.managers.ObjectManager;
import terra.model.SpawnPoint;
import terra.model.World;
import terra.model.object.ai.AbstractAI;
import terra.model.object.interfaces.Attacker;
import terra.model.object.knownlist.NpcKnownList;
import terra.model.object.knownlist.ObjectKnownList;
import terra.network.packet.server.NpcInfo;
import terra.util.Util;

public abstract class NpcObject extends LiveObject implements Attacker {
	private static final long serialVersionUID = 4941689562059550468L;

	private AbstractAI ai;
	private int npcId;
	private int attackRange = 16;
	private SpawnPoint spawn;

	public NpcObject(int objectId, NpcDef def) {
		super(objectId);
		setDef(def);
		getKnownList().setAutoUpdate(1000);
	}

	public NpcObject() {
		super(World.getNextId());
		getKnownList().setAutoUpdate(1000);
	}

	public void update(int delta) {
		if (!getAi().isWait())
			getAi().update(delta);
	}

	public void applyDef() {
		NpcDef def = DefManager.npcDefs.get(getNpcId());
		setImage(def.getStringProperty("image"));
		setName(def.getStringProperty("name"));
		setWidth(def.getIntProperty("width") == 0 ? Config.tileSize : def
				.getIntProperty("width"));
		setHeight(def.getIntProperty("height") == 0 ? Config.tileSize : def
				.getIntProperty("height"));
		getStat().setBaseHp(def.getIntProperty("baseHp"));
		getStat().setCon(def.getShortProperty("con"));
		getStat().setDex(def.getShortProperty("dex"));
		getStat().setStr(def.getShortProperty("str"));
		getStat().setBaseAttack(def.getIntProperty("damage"));
		getStat().setBaseAttackRate(def.getIntProperty("attackRate"));
		getStat().setBaseSpeed(def.getFloatProperty("speed"));
		// setSpeed(def.getFloatProperty("speed"));
		// setDamage(def.getIntProperty("damage"));
		setAttackRange(def.getIntProperty("attackRange"));
		// setAttackRate(def.getIntProperty("attackRate"));
		getAi().setAggressive(def.getBoolProperty("aggressive"));
		getAi().setCanfear(def.getBoolProperty("fear"));
		getStat().calcStats();
		getStat().setCurrentHp(getStat().getMaxHp());
	}

	@Override
	public void die(Attacker killer) {
		super.die(killer);
		if (spawn != null)
			spawn.objectDied();
		int x = (int) (getX() + getWidth() / 2) / Config.tileSize;
		int y = (int) (getY()) / Config.tileSize;
		int mapIndex = MapManager.getInstance().getMapIndex(x);
		int mapX = x - mapIndex * Config.mapWidth;
		Util.doItemDrop(DefManager.npcDefs.get(getNpcId()).drop, getX()
				+ getWidth() / 2, getY(), mapX, y, mapIndex);
	}

	public int getNpcId() {
		return npcId;
	}

	public void setDef(NpcDef def) {
		this.npcId = def.id;
	}

	public AbstractAI getAi() {
		return ai;
	}

	public void setAi(AbstractAI ai) {
		this.ai = ai;
	}

	public int getAttackRange() {
		return attackRange;
	}

	public void setAttackRange(int attackRange) {
		this.attackRange = attackRange;
	}

	@Override
	public ObjectKnownList getKnownList() {
		if (knownList == null)
			return knownList = new NpcKnownList(this);
		return knownList;
	}

	public int getDamage() {
		return (int) getStat().getAttackPower();
	}

	public int distanceFromSpawn() {
		return (int) distanceTo(getSpawn().getX(), getSpawn().getY());
	}

	public SpawnPoint getSpawn() {
		return spawn;
	}

	public void setSpawn(SpawnPoint spawn) {
		this.spawn = spawn;
	}

	public NpcInfo getInfo() {
		NpcInfo info = new NpcInfo();
		info.x = getX();
		info.y = getY();
		info.w = getWidth();
		info.h = getHeight();
		info.id = getNpcId();
		info.oId = getObjectId();
		info.maxHp = getStat().getMaxHp();
		info.hp = getStat().getCurrentHp();
		return info;
	}
	
	public void saveNpc(){
		try (Connection con = DatabaseFactory.getInstance().getConnection();
				PreparedStatement statement = con
						.prepareStatement("UPDATE npcs SET npcId=?,x=?,y=?,level=?,hp=?,instance=? WHERE objectId=?")) {
			statement.setInt(1, getNpcId());
			statement.setInt(2, (int) getX());
			statement.setInt(3, (int) getY());
			statement.setInt(4, getStat().getLevel());
			statement.setInt(5, getStat().getCurrentHp());
			statement.setString(6, this.getClass().getSimpleName());
			statement.setInt(7, getObjectId());
			statement.execute();
			statement.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static NpcObject loadNpc(ResultSet rs) throws SQLException{
		NpcDef d = DefManager.npcDefs.get(rs.getInt("npcId"));
		SpawnPoint spawn = new SpawnPoint();
		NpcObject npc = ObjectManager.createNpc(rs.getInt("objectId"),d);
		npc.setSpawn(spawn);
		spawn.setDef(d.id);
		spawn.setMapIndex(MapManager.getInstance().getMapIndex(rs.getInt("x")/Config.tileSize));
		spawn.setX(rs.getInt("spawn_x"));
		spawn.setY(rs.getInt("spawn_y"));
		spawn.spawnTime = rs.getInt("respawn_time");
		spawn.setSpawned(npc);
		npc.setX(rs.getInt("x"));
		npc.setY(rs.getInt("y"));
		npc.getStat().setCurrentHp(rs.getInt("hp"));
		return npc;
	}

	public void insertNpc() {
		try (Connection con = DatabaseFactory.getInstance().getConnection();
				PreparedStatement statement = con
						.prepareStatement("INSERT INTO npcs (objectId,npcId,x,y,level,hp,instance,respawn_time,spawn_x,spawn_y) VALUES (?,?,?,?,?,?,?,?,?,?)");) {
			statement.setInt(1, getObjectId());
			statement.setInt(2, getNpcId());			
			statement.setInt(3, (int) getX());
			statement.setInt(4, (int) getY());
			statement.setInt(5, getStat().getLevel());
			statement.setInt(6, getStat().getCurrentHp());
			statement.setString(7, this.getClass().getSimpleName());
			statement.setInt(8, getSpawn().spawnTime);
			statement.setInt(9, (int) getSpawn().getX());
			statement.setInt(10, (int) getSpawn().getY());
			statement.execute();
			statement.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
}
